import { cloneDeep } from 'lodash-es'
import { shallowRef, computed, proxyRefs } from 'vue'

// Composables
import { useRoute, Values } from '@/use/router'
import { useWatchAxios, AxiosParamsSource } from '@/use/watch-axios'

// Misc
import { isGroupSystem, isLogSystem, isEventSystem, isSpanSystem, SystemName } from '@/models/otel'
import { DataHint } from '@/org/types'

export interface System {
  system: string

  count: number
  errorCount: number
  rate: number
  errorRate: number
  groupCount: number
}

export type UseSystems = ReturnType<typeof useSystems>

const SORTED_SYSTEMS = [
  SystemName.All,
  SystemName.SpansAll,
  SystemName.LogAll,
  SystemName.EventsAll,
  'log',
  'log:trace',
  'log:debug',
  'log:info',
  'log:warn',
  'log:error',
  'log:fatal',
]

export function useSystems(axiosParamsSource: AxiosParamsSource) {
  const route = useRoute()

  const { status, loading, data } = useWatchAxios(
    () => {
      const { projectId } = route.value.params
      const req = {
        url: `/internal/v1/tracing/${projectId}/systems`,
        params: axiosParamsSource(),
        cache: true,
      }
      return req
    },
    { debounce: 0 },
  )

  const count = computed(() => {
    return data.value?.systems?.length ?? 0
  })

  const systems = computed((): System[] => {
    let systems: System[] = data.value?.systems ?? []

    systems = cloneDeep(systems)
    sortSystems(systems)
    addAllSystem(systems, SystemName.All)
    //addAllSystemForEachType(systems)
    return systems
  })

  const types = computed((): string[] => {
    return systemTypes(systems.value)
  })

  const dataHint = computed((): DataHint | undefined => {
    return data.value?.dataHint
  })

  const internalValue = shallowRef<string[]>([])

  const activeSystems = computed({
    get(): string[] {
      return internalValue.value
    },
    set(system: string | string[]) {
      if (Array.isArray(system)) {
        internalValue.value = system
      } else if (system) {
        internalValue.value = [system]
      } else {
        internalValue.value = []
      }
    },
  })

  const isSpan = computed(() => {
    return isSpanSystem(...activeSystems.value)
  })

  const groupName = computed(() => {
    if (isLogSystem(...activeSystems.value)) {
      return SystemName.LogAll
    }
    if (isEventSystem(...activeSystems.value)) {
      return SystemName.EventsAll
    }
    return SystemName.SpansAll
  })

  function reset(): void {
    activeSystems.value = []
  }

  function axiosParams() {
    return {
      system: activeSystems.value,
    }
  }

  function queryParams() {
    return { system: activeSystems.value }
  }
  function parseQueryParams(queryParams: Values) {
    activeSystems.value = queryParams.array('system')
  }

  return proxyRefs({
    status,
    loading,

    count,
    items: systems,
    types,
    dataHint,

    activeSystems,
    isSpan,
    groupName,

    reset,
    axiosParams,

    queryParams,
    parseQueryParams,
  })
}

// function addAllSystemForEachType(systems: System[]) {
//   if (!systems.length) {
//     return
//   }

//   const typeMap: Record<string, SystemTreeNode> = {}

//   for (let sys of systems) {
//     let typ = sys.system

//     const i = typ.indexOf(':')
//     if (i >= 0) {
//       typ = typ.slice(0, i)
//     }

//     const typeSys = typeMap[typ]
//     if (typeSys) {
//       typeSys.count += sys.count
//       typeSys.rate += sys.rate
//       typeSys.errorCount += sys.errorCount
//       typeSys.errorRate += sys.errorRate
//       typeSys.groupCount += sys.groupCount
//       typeSys.numChildren!++
//       continue
//     }

//     typeMap[typ] = {
//       ...sys,
//       system: typ,
//       numChildren: 1,
//     }
//   }

//   for (let systemType in typeMap) {
//     const typ = typeMap[systemType]
//     if (typ.numChildren <= 1) {
//       continue
//     }

//     for (let item of systems) {
//       if (item.system.startsWith(typ.system)) {
//         item.indent = true
//       }
//     }

//     systems.push({
//       ...typ,
//       system: typ.system + ':all',
//       isGroup: true,
//     })
//   }
// }

export function addAllSystem(systems: System[], systemName: string) {
  const index = systems.findIndex((item) => item.system === systemName)
  if (index >= 0) {
    return
  }

  const system = createAllSystem(systems, systemName)
  systems.unshift(system)
}

function createAllSystem(systems: System[], systemName: string): System {
  const allSystem = {
    system: systemName,
    count: 0,
    rate: 0,
    errorCount: 0,
    errorRate: 0,
    groupCount: 0,
  }
  for (let system of systems) {
    if (!isGroupSystem(system.system)) {
      allSystem.count += system.count
      allSystem.rate += system.rate
      allSystem.errorCount += system.errorCount
      allSystem.groupCount += system.groupCount
    }
  }
  allSystem.errorRate = allSystem.errorCount / allSystem.count
  return allSystem
}

export function systemTypes(systems: System[]): string[] {
  const types = new Set<string>()

  for (let sys of systems) {
    let systemType = sys.system

    const i = systemType.indexOf(':')
    if (i >= 0) {
      systemType = systemType.slice(0, i)
    }

    types.add(systemType)
  }

  return Array.from(types)
}

export function sortSystems(systems: System[]) {
  systems.sort((a, b) => {
    const ai = SORTED_SYSTEMS.indexOf(a.system)
    const bi = SORTED_SYSTEMS.indexOf(b.system)
    if (ai >= 0 && bi >= 0) {
      return ai - bi
    }
    if (ai >= 0) {
      return -1
    }
    if (bi >= 0) {
      return 1
    }
    return a.system.localeCompare(b.system)
  })
}
